/**
 * Copyright (c) 2012, Hakans Pirates - 
 * Johan Andersson spaiki17@gmail.com, 
 * Mattias Lundberg lundberg.mattias@gmail.com, 
 * Samuel Sjodin sjoodin@gmail.com
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the <organization> nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *  
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
package pirates.ship.cannon;

import com.jme3.app.SimpleApplication;
import com.jme3.asset.AssetManager;
import com.jme3.audio.AudioNode;
import com.jme3.bullet.PhysicsSpace;
import com.jme3.bullet.PhysicsTickListener;
import com.jme3.bullet.collision.PhysicsCollisionEvent;
import com.jme3.bullet.collision.PhysicsCollisionListener;
import com.jme3.bullet.collision.PhysicsCollisionObject;
import com.jme3.bullet.collision.shapes.CollisionShape;
import com.jme3.bullet.collision.shapes.SphereCollisionShape;
import com.jme3.bullet.control.RigidBodyControl;
import com.jme3.bullet.objects.PhysicsGhostObject;
import com.jme3.bullet.objects.PhysicsRigidBody;
import com.jme3.effect.ParticleEmitter;
import com.jme3.effect.ParticleMesh.Type;
import com.jme3.effect.shapes.EmitterSphereShape;
import com.jme3.material.Material;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector3f;
import java.util.Iterator;
import pirates.Settings;
import pirates.ship.ShipControl;

public class BallControl extends RigidBodyControl
		implements
			PhysicsCollisionListener,
			PhysicsTickListener {

	private float explosionRadius = 10;
	private PhysicsGhostObject ghostObject;
	private Vector3f vector = new Vector3f();
	private Vector3f vector2 = new Vector3f();
	private float forceFactor = 1;
	private ParticleEmitter fire;
	private float fxTime = 1;
	private float maxTime = 6;
	private float curTime = -1.0f;
	private float timer;
	private ParticleEmitter splash;
	private AudioNode splsound;
	private SimpleApplication app;
        private RigidBodyControl parent;

	public BallControl(RigidBodyControl parent, CollisionShape shape, float mass) {
		super(shape, mass);
		createGhostObject();
                this.parent = parent;
	}

	public BallControl(SimpleApplication app, RigidBodyControl parent, CollisionShape shape, float mass) {
		super(shape, mass);
		createGhostObject();
		this.app = app;
		prepareFire(app.getAssetManager());
		prepareSplash(app.getAssetManager());
                this.parent = parent;
	}

	@Override
	public void setPhysicsSpace(PhysicsSpace space) {
		super.setPhysicsSpace(space);
		if (space != null) {
			space.addCollisionListener(this);
		}
	}

	private void prepareFire(AssetManager assetManager) {
		int COUNT_FACTOR = 1;
		float COUNT_FACTOR_F = 1f;
		fire = new ParticleEmitter("Flame", Type.Triangle, 32 * COUNT_FACTOR);
		fire.setSelectRandomImage(true);
		fire.setStartColor(new ColorRGBA(1f, 0.4f, 0.05f,
				(float) (1f / COUNT_FACTOR_F)));
		fire.setEndColor(new ColorRGBA(.4f, .22f, .12f, 0f));
		fire.setStartSize(1.3f);
		fire.setEndSize(2f);
		fire.setShape(new EmitterSphereShape(Vector3f.ZERO, 1f));
		fire.setParticlesPerSec(0);
		fire.setGravity(0, -5f, 0);
		fire.setLowLife(.8f);
		fire.setHighLife(1);
		fire.setInitialVelocity(new Vector3f(0, 7, 0));
		fire.setVelocityVariation(1f);
		fire.setImagesX(2);
		fire.setImagesY(2);
		Material mat = new Material(assetManager,
				"Common/MatDefs/Misc/Particle.j3md");
		mat.setTexture("Texture",
				assetManager.loadTexture("Effects/Explosion/flame.png"));
		fire.setMaterial(mat);
	}

	private void prepareSplash(AssetManager assetManager) {
		int COUNT_FACTOR = 20;
		float COUNT_FACTOR_F = 1f;
		splash = new ParticleEmitter("Water", Type.Triangle, 32 * COUNT_FACTOR);
		splash.setSelectRandomImage(true);
		if (Settings.FANCY_WATER) {
			splash.setStartColor(new ColorRGBA(0f, 0.2f, 1f,
					(float) (1f / COUNT_FACTOR_F)));
			splash.setEndColor(new ColorRGBA(0f, .5f, .9f, 0f));
		} else {
			splash.setStartColor(new ColorRGBA(.8f, .8f, 1f,
					(float) (1f / COUNT_FACTOR_F)));
			splash.setEndColor(new ColorRGBA(1f, 1f, 1f, 0f));
		}
		splash.setStartSize(2.3f);
		splash.setEndSize(2f);
		splash.setShape(new EmitterSphereShape(Vector3f.ZERO, 1f));
		splash.setParticlesPerSec(0);
		splash.setGravity(0, -5f, 0);
		splash.setLowLife(.8f);
		splash.setHighLife(1.1f);
		splash.setInitialVelocity(new Vector3f(0, 12, 0));
		splash.setVelocityVariation(3f);
		splash.setImagesX(2);
		splash.setImagesY(2);
		Material mat = new Material(assetManager,
				"Common/MatDefs/Misc/Particle.j3md");
		mat.setTexture("Texture",
				assetManager.loadTexture("Effects/Explosion/flash.png"));
		splash.setMaterial(mat);

		splsound = new AudioNode(assetManager, "Sounds/Effects/splash.wav");
		splsound.setLooping(false);
		splsound.setReverbEnabled(true);
	}

	protected void createGhostObject() {
		ghostObject = new PhysicsGhostObject(new SphereCollisionShape(
				explosionRadius));
	}

	public void collision(PhysicsCollisionEvent event) {
		if (space == null) {
			return;
		}
                if (event.getObjectA() == parent || event.getObjectB() == parent) {
                    return;
                }
		if (event.getObjectA() == this || event.getObjectB() == this) {
			space.add(ghostObject);
			ghostObject.setPhysicsLocation(getPhysicsLocation(vector));
			space.addTickListener(this);
			if (fire != null && spatial.getParent() != null) {
				curTime = 0;
				fire.setLocalTranslation(spatial.getLocalTranslation());
				spatial.getParent().attachChild(fire);
				fire.emitAllParticles();
			}
			space.remove(this);
			spatial.removeFromParent();

			if (event.getNodeA().getControl(ShipControl.class) != null) {
				event.getNodeA().getControl(ShipControl.class).hitByBall(mass);
			} else if (event.getNodeB().getControl(ShipControl.class) != null) {
				event.getNodeB().getControl(ShipControl.class).hitByBall(mass);
			}
		}
	}

	public void prePhysicsTick(PhysicsSpace space, float f) {
		space.removeCollisionListener(this);
	}

	public void physicsTick(PhysicsSpace space, float f) {
		// get all overlapping objects and apply impulse to them
		for (Iterator<PhysicsCollisionObject> it = ghostObject
				.getOverlappingObjects().iterator(); it.hasNext();) {
			PhysicsCollisionObject physicsCollisionObject = it.next();
			if (physicsCollisionObject instanceof PhysicsRigidBody) {
				PhysicsRigidBody rBody = (PhysicsRigidBody) physicsCollisionObject;
				rBody.getPhysicsLocation(vector2);
				vector2.subtractLocal(vector);
				float force = explosionRadius - vector2.length();
				force *= forceFactor;
				force = force > 0 ? force : 0;
				vector2.normalizeLocal();
				vector2.multLocal(force);
				((PhysicsRigidBody) physicsCollisionObject).applyImpulse(
						vector2, Vector3f.ZERO);
			}
		}
		space.removeTickListener(this);
		space.remove(ghostObject);
	}

	@Override
	public void update(float tpf) {
		super.update(tpf);

		if (spatial.getLocalTranslation().y < 2) {
			if (splash != null && spatial.getParent() != null) {
				if (!Settings.IS_SERVER) {
					app.getAudioRenderer().playSource(splsound);
				}
				curTime = 0;
				splash.setLocalTranslation(spatial.getLocalTranslation());
				spatial.getParent().attachChild(splash);
				splash.emitAllParticles();
				fire = null;
			}
		}

		if (enabled) {
			timer += tpf;
			if (timer > maxTime) {
				if (spatial.getParent() != null) {
					space.removeCollisionListener(this);
					space.remove(this);
					spatial.removeFromParent();
				}
			}
		}
		if (enabled && curTime >= 0) {
			curTime += tpf;
			if (curTime > fxTime) {
				curTime = -1;
				fire.removeFromParent();
			}
		}
	}

	/**
	 * @return the explosionRadius
	 */
	public float getExplosionRadius() {
		return explosionRadius;
	}

	/**
	 * @param explosionRadius
	 *            the explosionRadius to set
	 */
	public void setExplosionRadius(float explosionRadius) {
		this.explosionRadius = explosionRadius;
		createGhostObject();
	}

	public float getForceFactor() {
		return forceFactor;
	}

	public void setForceFactor(float forceFactor) {
		this.forceFactor = forceFactor;
	}
}
